package pl.edu.agh.ssm.monitor.visualization;

import java.awt.geom.Rectangle2D;
import java.util.Iterator;

import prefuse.action.layout.Layout;
import prefuse.data.Tuple;
import prefuse.util.GraphicsLib;
import prefuse.visual.AggregateItem;
import prefuse.visual.AggregateTable;
import prefuse.visual.VisualItem;

/**
 * @author <a href="http://jheer.org">jeffrey heer</a>, Monika Nawrot
 * 
 * Layout for aggregates
 * 
 */

class AggregateLayout extends Layout {

	private int m_margin = 5; // convex hull pixel margin
	private double[] m_pts; // buffer for computing convex hulls

	public AggregateLayout(String aggrGroup) {
		super(aggrGroup);
	}

	/**
	 * @see edu.berkeley.guir.prefuse.action.Action#run(edu.berkeley.guir.prefuse.ItemRegistry,
	 *      double)
	 */
	@SuppressWarnings("unchecked")
	public void run(double frac) {

		try {
		
		AggregateTable aggr = (AggregateTable) m_vis.getGroup(m_group);

		int num = aggr.getTupleCount();
		if (num == 0)
			return;

		// update buffers
		int maxsz = 0;
		for (Iterator<Tuple> aggrs = aggr.tuples(); aggrs.hasNext();)
			maxsz = Math.max(maxsz, 4 * 2 * ((AggregateItem) aggrs.next())
					.getAggregateSize());
		if (m_pts == null || maxsz > m_pts.length) {
			m_pts = new double[maxsz];
		}

		// compute and assign convex hull for each aggregate
		Iterator aggrs = m_vis.visibleItems(m_group);
		while (aggrs.hasNext()) {
			AggregateItem aitem = (AggregateItem) aggrs.next();

			if (!aitem.isValid()) {
				continue;
			}
			
			int idx = 0;
			if (aitem.getAggregateSize() == 0)
				continue;
			VisualItem item = null;
			Iterator iter = aitem.items();
			while (iter.hasNext()) {
				item = (VisualItem) iter.next();
				if (item.isVisible()) {
					addPoint(m_pts, idx, item, m_margin);
					idx += 2 * 4;
				}
			}
			// if no aggregates are visible, do nothing
			if (idx == 0)
				continue;

			// compute convex hull
			double[] nhull = GraphicsLib.convexHull(m_pts, idx);

			// prepare viz attribute array
			float[] fhull = (float[]) aitem.get(VisualItem.POLYGON);
			if (fhull == null || fhull.length < nhull.length)
				fhull = new float[nhull.length];
			else if (fhull.length > nhull.length)
				fhull[nhull.length] = Float.NaN;

			// copy hull values
			for (int j = 0; j < nhull.length; j++)
				fhull[j] = (float) nhull[j];
			aitem.set(VisualItem.POLYGON, fhull);
			aitem.setValidated(false); // force invalidation
		}
		} catch (NullPointerException ex) {
			
		} catch (IllegalStateException ex) {
			
		} catch (IllegalArgumentException ex) {
			
		}
	}

	private static void addPoint(double[] pts, int idx, VisualItem item,
			int growth) {
		Rectangle2D b = item.getBounds();
		double minX = (b.getMinX()) - growth, minY = (b.getMinY()) - growth;
		double maxX = (b.getMaxX()) + growth, maxY = (b.getMaxY()) + growth;
		pts[idx] = minX;
		pts[idx + 1] = minY;
		pts[idx + 2] = minX;
		pts[idx + 3] = maxY;
		pts[idx + 4] = maxX;
		pts[idx + 5] = minY;
		pts[idx + 6] = maxX;
		pts[idx + 7] = maxY;
	}

} // end of class AggregateLayout
